package com.andaily.domain.cooking.settlement;

import com.andaily.domain.cooking.CookingAccount;
import com.andaily.domain.cooking.CookingAccountRepository;
import com.andaily.domain.cooking.CookingAccountStatus;
import com.andaily.domain.shared.Application;
import com.andaily.domain.user.User;
import com.andaily.domain.user.UserRepository;
import com.andaily.infrastructure.support.LogHelper;
import com.andaily.web.context.BeanProvider;

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/**
 * @author Shengzhao Li
 */
public class CookingSettlementGenerator {

    private static LogHelper log = LogHelper.create(CookingSettlementGenerator.class);

    private static CookingAccountRepository cookingAccountRepository() {
        return BeanProvider.getBean(CookingAccountRepository.class);
    }

    private static UserRepository userRepository() {
        return BeanProvider.getBean(UserRepository.class);
    }

    private String[] accountGuids;
    //Whether update account status to FINISHED,default is true
    private boolean updateStatus = true;

    public CookingSettlementGenerator(String... accountGuids) {
        this.accountGuids = accountGuids;
    }

    /**
     * Steps:
     * 1. 统计所有账务信息的总金额
     * 2.获取账务信息中的所有用户(distinct)
     * 3.分别统计每个用户已经支付的金额
     * 4.用总金额/用户数量 算出 平均金额
     * 5. 计算每个用户应该承担的金额 = 已经支付的金额 - 平均金额(>0 为应该收入的,<0 为需要支出的)
     * 6.生成  CookingSettlement 并保存.
     *
     * @return CookingSettlement
     */
    public CookingSettlement generate() {
        log.info("Generate from accounts: " + Arrays.toString(accountGuids));
        //1. 统计所有账务信息的总金额
        BigDecimal totalAmount = totalAmount();
        log.info("Total amount: " + totalAmount);
        CookingSettlement settlement = new CookingSettlement(totalAmount);
        //2.获取账务信息中的所有用户(distinct)
        List<User> users = users();
        int userSize = users.size();
        //4.用总金额/用户数量 算出 平均金额
        BigDecimal averageAmount = averageAmount(totalAmount, userSize);
        log.info("Average amount: " + averageAmount + ", user size: " + userSize);

        for (User user : users) {
            UserSettlement userSettlement = generateUserSettlement(user, averageAmount);
            settlement.userSettlements().add(userSettlement);
        }
        //  set accounts
        List<CookingAccount> accountList = accountList();
        for (CookingAccount cookingAccount : accountList) {
            SettlementCookingAccount settlementCookingAccount = new SettlementCookingAccount(settlement, cookingAccount);
            settlement.accounts().add(settlementCookingAccount);
        }
        //update status to  FINISHED
        if (updateStatus && !emptyAccountGuids()) {
            cookingAccountRepository().updateCookingAccountsStatus(accountGuids, CookingAccountStatus.FINISHED);
        }
        return settlement;
    }

    private List<CookingAccount> accountList() {
        if (emptyAccountGuids()) {
            return Collections.emptyList();
        }
        return cookingAccountRepository().findCookingAccountsByGuids(accountGuids);
    }

    private BigDecimal averageAmount(BigDecimal totalAmount, int userSize) {
        if (userSize == 0) {
            return totalAmount;
        }
        return totalAmount.divide(new BigDecimal(userSize), 2, BigDecimal.ROUND_HALF_EVEN);
    }

    private List<User> users() {
        List<String> guids = Application.cookingAccountUserGuids();
        return userRepository().findUserByGuids(guids);
    }

    private BigDecimal totalAmount() {
        if (emptyAccountGuids()) {
            return BigDecimal.ZERO;
        }
        return cookingAccountRepository().totalAmountOfCookingAccounts(accountGuids);
    }

    private boolean emptyAccountGuids() {
        return (accountGuids == null || accountGuids.length == 0);
    }

    private UserSettlement generateUserSettlement(User user, BigDecimal averageAmount) {
        UserSettlement userSettlement = new UserSettlement(user);
        //3.分别统计每个用户已经支付的金额
        BigDecimal userAmount = userAmount(user);
        //5. 计算每个用户应该承担的金额 = 已经支付的金额 - 平均金额(>0 为应该收入的,<0 为需要支出的)
        BigDecimal payableAmount = userAmount.subtract(averageAmount);
        log.info("User " + user.guid() + " total amount: " + userAmount + " ,payable amount: " + payableAmount);
        userSettlement.update(userAmount, payableAmount);
        return userSettlement;
    }

    private BigDecimal userAmount(User user) {
        if (emptyAccountGuids()) {
            return BigDecimal.ZERO;
        }
        BigDecimal amount = cookingAccountRepository().amountOfCookingAccountByUser(user.guid(), accountGuids);
        if (amount == null) {
            amount = BigDecimal.ZERO;
        }
        return amount;
    }

    public CookingSettlementGenerator setUpdateStatus(boolean updateStatus) {
        this.updateStatus = updateStatus;
        return this;
    }
}
